package com.tvunetworks.device.extra.service.impl;

import com.tvunetworks.center.common.exception.ServerException;
import com.tvunetworks.center.common.model.TokenPairConfirmParam;
import com.tvunetworks.center.common.model.UpdateTokenCodeParam;
import com.tvunetworks.center.common.util.Result;
import com.tvunetworks.center.common.util.ResultUtil;
import com.tvunetworks.device.extra.constants.ResultConstant;
import com.tvunetworks.device.extra.model.param.DeletePairParam;
import com.tvunetworks.device.extra.service.AnywhereService;
import com.tvunetworks.device.extra.service.feign.ChannelFeignService;
import com.tvunetworks.device.extra.service.feign.TokenFeignService;
import com.tvunetworks.device.extra.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Etan Yang
 * @create: 2021/11/22 13:03
 */

@Service
@Slf4j
public class AnywhereServiceImpl implements AnywhereService {

    private Map<String, Integer> codeMap = new ConcurrentHashMap<>();
    @Autowired
    private TokenFeignService tokenFeignService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ChannelFeignService channelFeignService;
    @Value("${device.extra.url}")
    private String deviceExtUrl;
    @Value("${tvucc.domain:10.12.22.93}")
    private String ccDomain;

    private static final String CODE_REDIS_KEY = "device:extra:token:code:";


    @Override
    public Result createTokenCode(UpdateTokenCodeParam param) {
        param.setName("DeviceExtra_" + System.currentTimeMillis());
        param.setExpireTime("4083291454000");
        String deviceExtUrl = this.deviceExtUrl;
        if (!StringUtils.startsWith(deviceExtUrl, "http")) {
            deviceExtUrl = "http://" + deviceExtUrl;
        }
        param.setCallbackUrl(deviceExtUrl +"/device-extra/base/pairConfirm");
        //1.check createTokenCode param
        this.checkCreateTokenCodeParam(param);
        //2.createToken
        try {
            Result<Object> token = tokenFeignService.createToken(param);
            if (!token.assertSuccess() || token.getResult() == null) {
                log.error("createTokenCode>>>>>>>>createTokenCode failed,param:[{}],result:[{}]", param, token);
                throw new ServerException(ResultConstant.Token.CREATE_TOKEN_FAILED);
            }
            Map<String, Object> result = new HashMap<>(2);
            result.put("ccDomain", ccDomain);
            result.put("code", token.getResult());
            token.setResult(result);
            redisUtil.set(CODE_REDIS_KEY + param.getUserId(), result, 48 * 60 * 60L);
            return token;
        } catch (Exception e) {
            log.error("TokenFeignService>>>>>createToken exception,param:[{}]", param, e);
            throw new ServerException(ResultConstant.Token.CREATE_TOKEN_FAILED);
        }
    }

    @Override
    public Result pairConfirm(Map<String,Object> param) {
        //1.check pair confirm param
        this.checkPairConfirmParam(param);
        TokenPairConfirmParam confirmParam = new TokenPairConfirmParam();
        confirmParam.setConfirm("Accept");
        confirmParam.setPeerId(param.get("device.peerId").toString());
        confirmParam.setCode(param.get("tokenCode.code").toString());
        //2.pair confirm to Token Service
        try {
            Result<Object> pairConfirm = tokenFeignService.pairConfirm(confirmParam);
            redisUtil.set(CODE_REDIS_KEY + confirmParam.getCode(), 1, 60 * 60L);
            return pairConfirm;
        } catch (Exception e) {
            log.error("pairConfirm>>>>>>>pairConfirm exception,param:[{}]", param, e);
            redisUtil.set(CODE_REDIS_KEY + confirmParam.getCode(), 2,  60 * 60L);
            throw new ServerException(ResultConstant.Token.TOKEN_PAIR_CONFIRM_FAILED);
        }
    }

    @Override
    public Object getCode(String userId) {
        return redisUtil.get(CODE_REDIS_KEY + userId);
    }

    @Override
    public Result deletePair(DeletePairParam param) {
        log.error("deletePair>>>>>>>param:[{}]", param);
        //1.删除channel中的event
        channelFeignService.setPlaceholderBySourceId(param.getUserId(), param.getSourceId(), param.getRid());
        return tokenFeignService.removePairByTRidAndOrigin(param.getRid(), param.getSourceId(), param.getOrigin());
    }

    @Override
    public int checkPairStatus(String code) {
        Object result = redisUtil.get(CODE_REDIS_KEY + code);
        if (result != null) {
            int status = Integer.parseInt(result.toString());
            return status;
        }
        return 0;
    }

    @Override
    public void removeAllTokenPair(String receiverId, String userId, int origin) {
        //1.根据receiverId获取所有的token配对
        List<String> anywhereIds = this.listTokenIdsByRid(receiverId, userId, origin);
        if (CollectionUtils.isNotEmpty(anywhereIds)) {
            anywhereIds.forEach(anywhereId-> tokenFeignService.removePairByTRidAndOrigin(receiverId, anywhereId, origin));
        }
    }
    @Override
    public List<String> listTokenIdsByRid(String rid, String userId, int origin) {
        try {
            List<String> rids = new ArrayList<>();
            rids.add(rid);
            List<String> tokenIds = tokenFeignService.listTokenPairTids(rids, userId, origin);
            if (CollectionUtils.isEmpty(tokenIds)) {
                log.error("listTokenIdsByRid>>>>>>list tokenIds is empty,rid:[{}],userId:[{}],origin:[{}]", rid, userId, origin);
                return new ArrayList<>();
            }
            return tokenIds;
        } catch (Exception e) {
            log.error("listTokenIdsByRid>>>>>>list tokenIds exception,rid:[{}],userId:[{}],origin:[{}],e:[{}]", rid, userId, origin, e);
            return new ArrayList<>();
        }
    }

    /**
     * check pair confirm param
     * @param param param
     */
    private void checkPairConfirmParam(Map<String,Object> param) {
        //1.check code
        Object code = param.get("tokenCode.code");
        if (code == null || StringUtils.isEmpty(code.toString())) {
            throw new ServerException(ResultUtil.paramError("Code can not be empty."));
        }
        //2.check peerId
        Object peerId = param.get("device.peerId");
        if (peerId == null || StringUtils.isEmpty(peerId.toString())) {
            throw new ServerException(ResultUtil.paramError("PeerId can not be empty."));
        }
    }

    /**
     * check createToken param
     * @param param param
     */
    private void checkCreateTokenCodeParam(UpdateTokenCodeParam param) {
        //3.check addPeerIds
        String willAddReceiverIds = param.getWillAddRIds();
        if (StringUtils.isEmpty(willAddReceiverIds)) {
            throw new ServerException(ResultUtil.paramError("WillAddRIds can not be empty."));
        }
    }

}
